[ Programming ]
Design Pattern
인터프리터 패턴디자인 패턴[디자인 패턴] 인터프리터 패턴
인터프리터 패턴(Interpreter Pattern) 인터프리터 패턴은 문법 규칙이 있는 어떤 언어를 해석하는 해석기를 만들 때 사용하는 행동 패턴이에요. 대표적으로 SQL Parser, 정규식 엔진, 구문 해석기(Typescript 같은 언어 해석) 같은 것들이 있어요. 인터프리터 패턴 구조 인터프리터 패턴은 크게 4가지 요소로 구성돼요. Abstract Expression , Terminal Expression, Nonterminal Expression, Context로 이루어져요. 모든 표현식이 따라야할 interpret 메서드를 정의해요. 문법 구조의 최소 인터페이스 역할을 해요. 문법에서 가장 기본 단위(토큰)을 해석하는 클래스에요. 숫자, 단어 같은 단일 토큰을 해석해요. Interpreter 패턴의 대부분이 이 Terminal Expression으로 구성돼요. 보통 문법 규칙을 조합하는 표현식 이에요. (+, -, *, %) 여러 Expression을 조합해서 더 큰 의미를 구성해요. 재귀적으로 하위 Expression을 interpret 호출해서 의미를 계산해요. 해석 과정에 필요한 데이터를 담고 있어요. 변수의 값, 환경 설정, 캐시 등을 저장해서 필요하면 Expression에 제공해요. 예시들 이번에는 코드 예시를 직접 가져오지는 않을게요. 왜냐하면 개념적인 이해가 더 중요하다고 생각하기 때문이에요. 이건 프로그래머로서 가장 기초적이고 중요한 개념이라고 생각해요. 모든 언어가 거의 같은 과정으로 CPU에서 실행 가능하도록 기계어로 혹은 인터프리터를 통한 실행을 위한 바이트 코드로 번역할 수 ...2025-11-28
역할 사슬 패턴디자인 패턴[디자인 패턴] 역할 사슬 패턴
역할 사슬 패턴 역할 사슬 패턴은 요청을 처리할 수 있는 객체가 여러 개일 경우, 요청을 처리할 수 있는 객체들을 동적으로 연결해서 처리하도록 할 수 있는 행위 패턴이에요. 이 패턴은 이해가 쉽고 구조도 단순해서 글이 많이 짧을 것 같아요. 역할 사슬 패턴 구조 역할 사슬 패턴은 Handler, ConcreteHandler, Request 로 이루어져 있다고 보면되요. 각 클래스의 역할을 살펴봐요. Handler (추상 클래스 또는 인터페이스) 역할: 요청을 처리하는 인터페이스에요. 다음 핸들러를 지정하는 setNext 메서드, 요청을 처리하는 handle 메서드가 있어요. ConcreteHandlerX 역할: Handler를 구현하는 구상 클래스에요. 요청을 처리할 수 있으면 처리하고 아니면 next.handle(request)로 넘겨요. Request 역할: 처리할 요청 객체에요. handler들이 이 request를 보고 처리 여부를 확인할 거에요. 위 그림은 Client에서 request를 Handler 인터페이스로 처리하도록 요청할 경우 처리 되는 Flow Chart에요. 되게 쉽죠? 역할 사슬 패턴 예시 이메일 분류 기능을 역할 사슬 패턴으로 구현하는 예시를 보여주려고 해요. 핸들러가 스팸, 팬, 법적 메일을 처리해야하는데 각각에 대해 별도의 핸들러에서 전문적으로 처리하도록 만들거고, 체인을 구성하는 것을 보여줄 거에요. // 이메일 요청 객체 class Email { constructor(public subject: string, public body: string) {} } // 추상 H...2025-11-21
빌더 패턴디자인 패턴[디자인 패턴] 빌더 패턴
빌더 패턴(Builder Pattern) 빌더 패턴은 복잡하게 여러 단계로 나눠는 객체 생성 과정을 캡슐화하고 싶을때 쓰는 생성 패턴이에요. 객체 생성 로직이 복잡한 경우나 동일한 구조에서 서로 다른 설정으로 객체를 만들고 싶은 경우 유용해요. SQL을 코드로 작성하는 경우, 프론트엔드는 zod라는 타입 검증을 만드는 라이브러리에서 많이 사용되요. 빌더 패턴 구조 (Director 사용하는 빌더패턴) Director 방식을 사용하는 빌더 패턴은 복잡한 생성 과정을 제어하는 Director, 제품 생성에 필요한 방법을 추상화한 인터페이스인 Builder, Builder 인터페이스를 구현한 ConcreteBuilder, 그리고 결과물인 Product로 구성되요. Director 역할: Builder를 사용해서 객체 생성 순서와 과정을 제어하는 역할이에요. Client 대신 복잡한 생성 로직을 관리해요. 특징: 어떤 ConcreteBuilder가 사용될지 모르지만, Builder 인터페이스만 알고 있으면 일관된 방식으로 객체를 만들 수 있어요. 예시: 건축 감독 → 어떤 집을 짓든 "기초 → 골조 → 벽 → 지붕" 순서로 지시해요. Builder 역할: 복잡한 객체를 생성하는데 필요한 단계들을 추상 메서드로 정의하는 인터페이스에요. 특징: Product를 만드는 최소 기준을 정의하고, ConcreteBuilder들이 따라야 할 공통 API를 제공해요. 예시: 집 짓기 설계도 → buildFoundation(), buildWalls(), buildRoof() 등의 단계를 정의해요. ConcreteBuilder 역할:...2025-11-14
브릿지 패턴디자인 패턴[디자인 패턴] 브릿지 패턴
브릿지 패턴(Bridge Pattern) 브릿지 패턴은 추상화 계층(Abstraction)과 구현 계층(Implementor)를 분리해서 독립적으로 확장할 수 있게 만드는 구조 패턴이에요. 추상화 계층을 기능 클래스 계층이라고 보기도 해요. 그래서 기능을 추가하고 싶으면 추상화 계층을, 구현을 추가하고 싶으면 구현 계층을 활용하면 되니 독립적으로 확장이 가능하게 되는 거에요. 브릿지 패턴 구조 Abstraction(추상화 계층, 기능 계층)과 Implementor(구현 계층)을 분리하여 이어주는 구조가 되기 때문에 브릿지 패턴이에요. 두 계층을 직접 묶지 않고, 중간에 다리를 놓아 서로 독립적으로 발전할 수 있도록 이은 거에요. 그림의 각 요소에 대해 설명해 볼게요. Abstraction (추상화 계층의 상위 역할, 기능 계층의 상위 역할) 역할: Client가 사용하는 추상 인터페이스로 내부에 Implementor를 참조하여 구현 일부를 위임해요. 특징: 어떤 Implementor를 사용할 지 모르지만, 단지 호출하여 기능을 만드는데 사용해요. 예시: 리모컨 -> TV가 삼성인지 LG인지 모르지만 "전원 키기", "전원 끄기"가 가능해요. RefinedAbstraction (추상화 계층의 구현 클래스) 역할: Abstraction에 정의된 operation()을 실제로 구현 및 확장해요. 여전히 Implementor를 사용해여 구현해요. 특징: Abstraction을 상속받기 때문에 기능 계층과 약한 결합을 가져요. 예시: LG 리모콘, 종합 리모콘 등.. Implementor (구현 계층의 인터페이스...2025-11-14
디자인 패턴컴파운드 패턴[디자인 패턴] 컴파운드 패턴
컴파운드 패턴 오늘은 여러 패턴의 조합으로 문제를 해결하는 컴파운드 패턴에 대해서 알아봐요. 컴파운드 패턴은 딱 정해진 형식이 없어요. 단순히 여러 패턴을 조합하여 문제를 해결하는 것이에요. 대표적인 예로 MVC, MVP, VIPER과 같은 많은 아키텍처들이 있어요. 그런데 Head Frist 디자인 패턴 책처럼 한 단계씩 설명하기는 글 내용이 너무 길어질 것 같아요. 그래서 통째로 설명할 수 있게 예시 코드를 통해 설명하고자 해요. 컴파운드 패턴 예시 우선 컴파운터 패턴 예시 코드를 만들기 위해 배경이 필요해요. 피그마 기능을 컴파운드 패턴으로 구현해보자 저가 만들고 싶은 기능은 피그마처럼 화면에 요소를 추가하여 꾸밀수 있는 기능이에요. 생각한 기능들은 다음과 같아요. 기능 정의 화면에 삼각형, 사각형 같은 요소 추가가 가능하다. 추가된 요소는 이동 가능하다. 이동 했던 동작들은 다시 뒤로 되돌릴 수 있다. 여러 사람이 작업하기 때문에 요소에 대한 소유권이 필요하다. 그러면 각 기능을 위해 무엇이 필요할까요? 정리해봐요 필요한 코드들 Viewer에 삼각형, 사각형 등을 렌더링 해야한다. Viewer와 상호 작용 가능한 요소가 필요하다. 상호 작용을 통해 데이터의 수정(위치 이동)할 수 있는 기능이 필요하다. 데이터가 수정되면 수정된 데이터를 Viewer에 전달하여 렌더링할 수 있어야 한다. Viewer에서 요소를 이동시키려할 때 소유권을 가져 다른 사람이 함께 이동시키지 못하게 해야한다. 이동 했던 이력을 저장하여 되돌릴 수 있어야한다. 그러면 위 코드들을 보고 어떠한 패턴들을 사용해야할까요? 사람마다 다를...2025-10-31
프록시 패턴[디자인 패턴] 프록시 패턴
프록시 패턴(Proxy Pattern) 프록시 패턴은 어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 행동 패턴이에요. 원격, 가상, 보호, 캐싱, 로깅 프록시와 같이 다양한 종류가 있어요. 이런 프록시들의 목적을 하나로 요약하면 자원 접근 제한 및 통제에요. 대표적인 예로 자바나 자바스크립트의 Class로 만든 Instance의 자원 접근을 Proxy를 통해서 부가 적인 기능을 수행할 수 있게 만드는 것이에요. 프록시 패턴 구조 프록시는 특정 객체에 대한 접근 제어를 하기 위해 사용되고 특정 객체는 Subject라고 해요. 프록시(Proxy)는 Subject 인터페이스를 따라 구현하여 실제 RealSubject와 완전히 호환되도록 바꿀수 있어요. 이렇게 구현하면 Client가 사용할 때 기존의 Subject 인터페이스를 그대로 사용하며 Proxy가 어떻게 생겼는지 몰라도 사용할 수 있어요. Subject : RealSubject가 구현하는 인터페이스에요. 아마 프록시를 추가한다는 것은 이미 Subject가 있을 확률이 높을 거라 생각해요. RealSubject : 접근 제어할 원본 객체이며, 실제 핵심 작업을 수행하는 객체에요. Proxy : RealSubject에 대한 접근을 제어할 중개자(대리인) 프록시 패턴 시퀀스 다이어그램 위 그림은 실제 Client가 Proxy 패턴으로 코드를 사용하면 어떤 순서로 코드가 실행될 지 보여줘요. Proxy의 request() 메서드를 사용하면, RealSubject의 알고리즘을 대신 위임하여 사용하고 그 값을 리턴해주거나 자...2025-10-19
상태 패턴디자인 패턴[디자인 패턴] 상태 패턴
상태 패턴(State Pattern) 상태 패턴을 구현해서 객체의 상태에 따라 다르게 동작하는 코드를 깔끔하게 관리하는 방법을 알려드려요. 이 패턴으로 복잡한 조건문 없이도 상태별로 다른 행동을 쉽게 구현할 수 있어요. 상태 패턴이 왜 필요한가요? 실무에서 이런 상황을 자주 마주칠 수 있어요. 커머스 플랫폼에서 주문 상태를 관리하는데, 주문 상태에 따라 할 수 있는 행동이 달라요. 예를 들어 배송 중인 주문은 취소할 수 없지만, 배송 준비 중인 주문은 취소할 수 있죠. 조건문으로 상태 관리하는 코드 class Order { constructor(orderId) { this.orderId = orderId; this.state = 'ORDERED'; // 주문 완료 } cancel() { if (this.state === 'ORDERED') { console.log('주문이 취소되었습니다.'); this.state = 'CANCELLED'; } else if (this.state === 'PREPARING') { console.log('배송 준비 중인 주문이 취소되었습니다.'); this.state = 'CANCELLED'; } else if (this.state === 'SHIPPING') { console.log('배송 중인 주문은 취소할 수 없습니다.'); } else if (this.state === 'DELIVERED') { console.log('배송 완료된 주문은 취소할 수 없...2025-10-10
Composite 패턴[디자인 패턴] Composite 패턴
Composite 패턴 Composite 패턴을 구현해서 개별 객체와 객체 그룹(부분-전체 계층구조)를 동일한 방식으로 다루는 방법을 알려드려요. 이 패턴으로 트리 구조의 복잡한 계층을 단순하게 처리하고, 부분과 전체를 구별하지 않고 일관된 코드를 작성할 수 있어요. Composite 패턴이 왜 필요한가요? 실무에서는 보통 카테고리같은 기능을 구현하는데 자주 마주치는 문제에요. 만약 파일 시스템을 만든다고 예를 들어봐요. 파일 시스템을 만드는데 파일과 폴더를 따로 처리해야 해요. 폴더는 파일을 담을 수 있고, 폴더 안에 또 폴더가 들어갈 수 있죠. 그런데 용량을 계산하거나 출력할 때마다 파일인지 폴더인지 확인하는 코드를 작성해야 해요. // Composite 패턴 없이 구현한 파일 시스템 class File { constructor(name, size) { this.name = name; this.size = size; } getSize() { return this.size; } } class Folder { constructor(name) { this.name = name; this.children = []; } add(item) { this.children.push(item); } getSize() { let totalSize = 0; for (const child of this.children) { // 파일인지 폴더인지 매번 확인해야 함 if (child instanceof F...2025-10-03
Iterator 패턴[디자인 패턴] Iterator 패턴
Iterator 패턴 Iterator 패턴을 구현해서 서로 다른 자료구조를 동일한 방식으로 순회하는 방법을 알려드려요. 이 패턴으로 자료구조가 달라도 클라이언트 코드를 수정하지 않고 일관된 방식으로 데이터에 접근할 수 있어요. Iterator 패턴이 왜 필요한가요? 실무에서 이런 상황을 자주 마주칠 수 있어요. 두 개발팀이 비슷한 기능을 각각 개발했는데, 한 팀은 배열을 사용하고 다른 팀은 연결 리스트를 사용했어요. 이제 두 데이터를 합쳐서 보여줘야 하는데 문제가 생겨요. A팀 B팀 class ArrayInventory { constructor() { this.items = []; } addItem(item) { this.items.push(item); } getItems() { return this.items; } } class Node { constructor(data) { this.data = data; this.next = null; } } class LinkedListInventory { constructor() { this.head = null; } addItem(item) { const newNode = new Node(item); if (!this.head) { this.head = newNode; return; } let current = this.head; while (current.next) { current = curre...2025-10-03

